package com.allwees.bs.c.module.ops.util;

import lombok.extern.slf4j.Slf4j;

import java.util.Random;

/**
 * 用户邀请码工具类
 * <ol>
 * <li>邀请码由6位可重复数字字母组成</li>
 * <li>用户id和邀请码可以相互转化</li>
 * </ol>
 *
 * @author yangylsky
 * @since 2019-10-26
 *
 */
@Slf4j
public class UserCouponCodeUtil {
	/**
	 * 自定义进制(0,1没有加入,容易与o,l混淆)，数组顺序可进行调整增加反推难度，A用来补位因此此数组不包含A，共31个字符。
	 */
	private static final char[] BASE = new char[] {'H', 'V', 'E', '8', 'S', '2', 'D', 'Z', 'X', '9', 'C', '7', 'P', '5', 'K', '3', 'M', 'J',
			'U', 'F', 'R', '4', 'W', 'Y', 'L', 'T', 'N', '6', 'B', 'G', 'Q'};

	/**
	 * A补位字符，不能与自定义重复
	 */
	private static final char SUFFIX_CHAR = 'A';

	/**
	 * 进制长度
	 */
	private static final int BIN_LEN = BASE.length;

	/**
	 * 生成邀请码长度
	 */
	public static final int CODE_LEN = 8;

	public static final int MAX_NUMBER = (int) Math.pow(BIN_LEN, CODE_LEN) - 1;

	private static boolean TRANS_POSITION = true;

	/**
     * ID转换为邀请码
     *
     * @param id
     * @return
     */
    public static String idToCode(long id) {
		if(id < 1 || id > MAX_NUMBER) {
			throw new IllegalArgumentException("param[value]'s range is 1 <= value <= " + MAX_NUMBER);
		}
        char[] buf = new char[BIN_LEN];
        int charPos = BIN_LEN;

        long transedId = _transposition(id);
        // 当id除以数组长度结果大于0，则进行取模操作，并以取模的值作为数组的坐标获得对应的字符
        while (transedId / BIN_LEN > 0) {
            int index = (int) (transedId % BIN_LEN);
            buf[--charPos] = BASE[index];
            transedId /= BIN_LEN;
        }

        buf[--charPos] = BASE[(int) (transedId % BIN_LEN)];
        // 将字符数组转化为字符串
        String result = new String(buf, charPos, BIN_LEN - charPos);

        // 长度不足指定长度则随机补全
        int len = result.length();
        if (len < CODE_LEN) {
            StringBuilder sb = new StringBuilder();
            sb.append(SUFFIX_CHAR);
            Random random = new Random();
            // 去除SUFFIX_CHAR本身占位之后需要补齐的位数
            for (int i = 0; i < CODE_LEN - len - 1; i++) {
                sb.append(BASE[random.nextInt(BIN_LEN)]);
            }

            result += sb.toString();
        }
//		log.info("{} -> {}", id, result);
        return result;
    }

	/**
	 * 邀请码解析出id<br/>
	 * 基本操作思路恰好与idToCode反向操作。
	 *
	 * @param code
	 * @return
	 */
	public static long codeToId(String code) {
		char[] charArray = code.toCharArray();
		long result = 0;
		for(int i = 0; i < charArray.length; i++) {
			int index = 0;
			for(int j = 0; j < BIN_LEN; j++) {
				if(charArray[i] == BASE[j]) {
					index = j;
					break;
				}
			}

			if(charArray[i] == SUFFIX_CHAR) {
				break;
			}

			if(i > 0) {
				result = result * BIN_LEN + index;
			} else {
				result = index;
			}
		}
		return _transposition(result);
	}

	/**
	 * 交换数值（位数足够的话），算法：
	 * 1.第2位和倒数第1位交换；
	 * 2.第3位和倒数第2位交换；
	 * 2.第4位和倒数第3位交换
	 *
	 * @param value
	 * @return
	 */
	private static long _transposition(long value) {
		if(!TRANS_POSITION) {
			return value;
		}
		char[] vChars = String.valueOf(value).toCharArray();
		if(value > 100) {
			_transposition(vChars, 1, vChars.length - 1);
		}
		if(value > 10000) {
			_transposition(vChars, 2, vChars.length - 2);
		}
		if(value > 1000000) {
			_transposition(vChars, 3, vChars.length - 3);
		}
		long transValue = Long.valueOf(String.valueOf(vChars));
		//返回换位后的值（如果换位后大于MAX_NUMBER，则取消换位，返回原值）
		return transValue > MAX_NUMBER ? value : transValue;
	}

	/**
	 * 互换第pos1个字符和第pos2个字符
	 *
	 * @param vChars
	 * @param pos1
	 * @param pos2
	 */
	private static void _transposition(char[] vChars, int pos1, int pos2) {
		char c = vChars[pos1];
		vChars[pos1] = vChars[pos2];
		vChars[pos2] = c;
	}
}
